#ifndef __ECLOG_H__
#define __ECLOG_H__

#include "atomic.h"
#include "table_proto.h"
#include "fileinfo.h"
#include "fs_proto.h"
#include "chunk.h"
#include "plock.h"
#include "cluster.h"
#include "lease.h"
#include "../replica/replica.h"

/**
 * ec volume layout:
 * +------------+------------------+------------+------------------+------------------
 * | eclog area | volume data area | eclog area | volume data area |
 * |    1GB     |        10GB      |    1GB     |        10GB      |
 * +------------+------------------+------------+------------------+------------------
 *
 * eclog layout:
 *                     metadata : data = sizeof(ec_md_t) : PAGE_SIZE ~= 32 : 4096
 * +--------+-------------------+---------------------------+
 * |  head  |   metadata area   |        data area          |
 * | +----+ | +----+ +----+     | +--------+ +--------+     |
 * | |    | | |seg0| |seg1| ... | |  seg0  | |  seg1  | ... |
 * | +----+ | +----+ +----+     | +--------+ +--------+     |
 * +--------+--^----------------+----^----------------------+
 *             |                     |`----------------------.
 *             |  ec_md_t            |      .----> chunk_io  |
 *             `- +----------+       |      |      +----+ ---`
 *                |seg_offset|-------`      |      |data|
 *                |chkid     |--------------`      +----+
 *                |offset    |---------------------^    ^
 *                |size      |--------------------------`
 *                +----------+
*/

#if 0
#define ECLOG_ALIGN_SIZE LICH_BLOCK_SIZE
#else
#define ECLOG_ALIGN_SIZE PAGE_SIZE
#endif

#define __ECLOG_MAGIC__ 0x866aa9f3
#define ECLOG_SEG_SIZE(size)  (8 * M2B)

//#define ECLOG_ENABLE TRUE

#define ECLOG_LOG_SIZE_ALIGN(ec) (LLU)_align_down((LLU)(ec->log_size * G2B), LICH_CHUNK_SPLIT)
#define ECLOG_LOG_CHUNK_COUNT(ec) (ECLOG_LOG_SIZE_ALIGN(ec) / LICH_CHUNK_SPLIT)

#define ECLOG_DATA_SIZE_ALIGN(ec) (LLU)_align_down((LLU)(ec->data_size * G2B), (ec->k * LICH_CHUNK_SPLIT))
#define ECLOG_DATA_CHUNK_COUNT(ec) (ECLOG_DATA_SIZE_ALIGN(ec) / (ec->k * LICH_CHUNK_SPLIT))

#define ECLOG_SECTION_SIZE_ALIGN(ec) (LLU)(ECLOG_LOG_SIZE_ALIGN(ec) + ECLOG_DATA_SIZE_ALIGN(ec))
#define ECLOG_SECTION_CHUNK_COUNT(ec) (ECLOG_LOG_CHUNK_COUNT(ec) + ECLOG_DATA_CHUNK_COUNT(ec))

#define ECLOG_LOG_MAX(ec) (VOLUME_SIZE_MAX / ECLOG_DATA_SIZE_ALIGN(ec))
#define ECLOG_LOG_COUNT(size, ec) (_ceil(size, ECLOG_DATA_SIZE_ALIGN(ec)))

typedef struct {
        int eclogid;
        int segid;
        uint64_t offset;
} ecloc_t;

typedef struct {
        chkid_t chkid;
        uint64_t size;
        uint64_t offset;
        uint32_t data_crc;
        uint32_t meta_crc;
        uint64_t seg_offset;
} ec_md_t;

typedef struct {
        /*static data*/
        uint64_t md_offset;
        uint64_t data_offset;
        uint64_t segid;

        /*variables data*/
        atomic_t dirty;
        uint64_t cur;
        uint64_t md_count;
        ec_md_t *md;
} eclog_seg_t;

typedef struct {
        uint64_t seg_used;
        uint32_t seg_status;
} eclog_seginfo_t;

typedef struct {
        uint32_t magic;
        uuid_t uuid;
        uint64_t seg_idx;
        uint64_t cursor;
        eclog_seginfo_t seg_info[0];
} eclogmd_t;

typedef struct {
        uint64_t offset;
        uint64_t size;

        uint64_t wb_size;
        uint64_t md_total;

        uint64_t md_head;
        uint64_t seg_md_size;
        uint64_t seg_md_count;

        uint64_t seg_len;
        uint64_t seg_count;
} ecloginfo_t;

struct __volume_proto;
typedef struct __eclog_t {
        int eclogid;
        plock_t rwlock;

        struct __volume_proto *volume_proto;
        ecloginfo_t ecloginfo;
        eclogmd_t *eclogmd;
        eclog_seg_t *seg;
} eclog_t;

#define __GET_ECLOG_MD_HEAD__(size) ECLOG_SEG_SIZE(size)
#define __GET_ECLOG_MD_OFFSET__(size) __GET_ECLOG_MD_HEAD__(size)
#define __GET_ECLOG_MD_SIZE__(size) (_align_down(((size - __GET_ECLOG_MD_HEAD__(size)) * sizeof(ec_md_t)) / PAGE_SIZE, PAGE_SIZE))
#define __GET_ECLOG_MD_TOTAL__(size) (__GET_ECLOG_MD_HEAD__(size) + __GET_ECLOG_MD_SIZE__(size))

#define __GET_ECLOG_SIZE__(size) (_align_down(size - __GET_ECLOG_MD_TOTAL__(size), PAGE_SIZE))

#define __GET_ECLOG_SEG_LEN__(size) ECLOG_SEG_SIZE(size)
#define __GET_ECLOG_SEG_COUNT__(size) (__GET_ECLOG_SIZE__(size) / __GET_ECLOG_SEG_LEN__(size))

#define __GET_ECLOG_SEG_MD_COUNT__(size) (__GET_ECLOG_SEG_LEN__(size) / PAGE_SIZE)
#define __GET_ECLOG_SEG_MD_SIZE__(size) (__GET_ECLOG_SEG_MD_COUNT__(size) * sizeof(ec_md_t))

#define ECLOG_MD_SIZE(seg_count) (sizeof(eclogmd_t) + seg_count * sizeof(eclog_seginfo_t))

int eclog_load(struct __volume_proto *volume_proto);

int eclog_chunk_islog(const chkid_t *chkid, const ec_t *ec);
int eclog_chunk_skiplog(const chkid_t *chkid, const ec_t *ec);
int eclog_chunk_redo(struct __volume_proto *volume_proto, chkinfo_t *chkinfo, chkstat_t *chkstat,
                const ec_t *ec, clockstat_t *clocks, unsigned char *src_in_err);

int chunk_proto_eclog_clean(eclog_t *eclog, ecloc_t *ecloc);
int chunk_proto_eclog_write(struct __volume_proto *volume_proto, const io_t *io, const ec_t *ec,
                uint32_t off, uint32_t count, buffer_t *strips, eclog_t **_eclog, ecloc_t *_loc);

#endif /* __ECLOG_H__ */

